package com.ken.zeratul.commons.spring;

import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * Spel表达式解析器
 */
@Slf4j
public class SpelUtils  {

    /**
     * Spel表达式解析器
     */
    private static ExpressionParser expressionParser = new SpelExpressionParser();

    private static LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();


    /**
     * 根据方法实参解析Spel表达式
     * @param spel
     * @param method
     * @param args
     * @param cls
     * @param defaultValue
     * @return
     * @param <T>
     */
    public static <T> T parseExpression(String spel, Method method, Object[] args, Class<T> cls, T defaultValue){
        return parseExpression(spel, null, null, null, method, args, null, cls, defaultValue);
    }

    /**
     * 根据方法实参解析Spel表达式
     * @param spel
     * @param method
     * @param args
     * @param cls
     * @param defaultValue
     * @return
     * @param <T>
     */
    public static <T> T parseExpression(String spel, Object targetObject, Method method, Object[] args, Class<T> cls, T defaultValue){
        return parseExpression(spel, targetObject, null, null, method, args, null, cls, defaultValue);
    }

    /**
     * 根据自定义数据数据解析Spel表达式
     * @param spel
     * @param data - 自定义数据
     * @param cls
     * @param defaultValue
     * @return
     * @param <T>
     */
    public static <T> T parseExpression(String spel, Object data, Class<T> cls, T defaultValue) {
        return parseExpression(spel,null, data, null, null, null, null, cls, defaultValue);
    }

    /**
     * 根据自定义数据数据解析Spel表达式
     * @param spel
     * @param data - 自定义数据
     * @param cls
     * @param defaultValue
     * @return
     * @param <T>
     */
    public static <T> T parseExpression(String spel, Object targetObject, Object data, Class<T> cls, T defaultValue) {
        return parseExpression(spel, targetObject, data, null, null, null, null, cls, defaultValue);
    }

    /**
     * 解析Spel表达式
     * @param spel
     * @param targetObject
     * @param data
     * @param extras
     * @param method
     * @param args
     * @param registerMethods
     * @param cls
     * @param defaultValue
     * @return
     * @param <T>
     */
    public static <T> T parseExpression(String spel,
                                        Object targetObject,
                                        Object data,
                                        Map<String, Object> extras,
                                        Method method,
                                        Object[] args,
                                        List<RegisterMethod> registerMethods,
                                        Class<T> cls,
                                        T defaultValue){
        //解析器的上下文对象
        StandardEvaluationContext context = new StandardEvaluationContext(targetObject);

        //判断是否存在方法对象
        if (ObjectUtil.isAllNotEmpty(method, args)) {
            String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(method);
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
        }

        //判断自定义拓展属性是否为空
        if (ObjectUtil.isNotEmpty(extras)) {
            for (Map.Entry<String, Object> entry : extras.entrySet()) {
                context.setVariable(entry.getKey(), entry.getValue());
            }
        }

        //判断自定义数据是否为空
        if (ObjectUtil.isNotNull(data)) {
            context.setVariable("value", data);
        }

        //设置bean解析器 为Spring容器
        context.setBeanResolver(new BeanFactoryResolver(SpringUtils.getContext()));

        //获取请求参数的方法
        if (ObjectUtil.isNotEmpty(registerMethods)) {
            for (RegisterMethod registerMethod : registerMethods) {
                try {
                    context.registerFunction(ObjectUtil.isNotEmpty(registerMethod.getExecMethodName()) ? registerMethod.getExecMethodName() : registerMethod.getMethodName(),
                            registerMethod.getRegisterMethodObjCls().getMethod(registerMethod.getMethodName(),
                                    registerMethod.getParamClasses().toArray(new Class[0])));
                } catch (NoSuchMethodException e) {
                    log.error("Spel表达式注册方法失败, 方法名: {}", registerMethod.getMethodName(), e);
                }
            }
        }

        try {
            //执行表达式解析
            Expression expression = expressionParser.parseExpression(spel);
            return expression.getValue(context, cls);
        } catch (Exception e) {
            log.error("Spel表达式解析异常, spel: {}, data: {}", spel, args, e);
            return defaultValue;
        }
    }
}
